﻿using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Windows.Forms;
using SharpPcap;
using PacketDotNet;
using System.Net.NetworkInformation;
using log4net;

namespace TinyIM2013
{
    public partial class CaptureForm : Form
    {
        private int packetCount;
        private BindingSource bs;
        private ICaptureDevice device;
        private bool BackgroundThreadStop;
        private DateTime LastStatisticsOutput;
        private DeviceListForm deviceListForm;
        private object QueueLock = new object();
        private Queue<TinyPacket> TinyPacketQueue;
        private bool statisticsUiNeedsUpdate = false;
        private ICaptureStatistics captureStatistics;
        private int CurrentSelectedIndexOfListView = -1;
        private System.Threading.Thread backgroundThread;
        private List<RawCapture> PacketQueue = new List<RawCapture>();
        private TimeSpan LastStatisticsInterval = new TimeSpan(0, 0, 2);
        private log4net.ILog AppLog = log4net.LogManager.GetLogger("TinyIM.Application");
        private log4net.ILog NetworkLog = log4net.LogManager.GetLogger("TinyIM.Application");
        public CaptureForm()
        {
            InitializeComponent();
        }

        void uiListViewPacket_SelectedIndexChanged(object sender, EventArgs e)
        {
            ListView listView = sender as ListView;
            if (listView.SelectedIndices.Count > 0)
            {
                if (listView.SelectedIndices[0] == CurrentSelectedIndexOfListView)
                {
                    return;
                }
                else
                {
                    CurrentSelectedIndexOfListView = listView.SelectedIndices[0];
                }

            }
        }
        void deviceListForm_OnItemSelected(int itemIndex)
        {
            // close the device list form
            deviceListForm.Hide();

            StartCapture(itemIndex);
        }

        void deviceListForm_OnCancel()
        {
            Application.Exit();
        }


        private PacketArrivalEventHandler arrivalEventHandler;
        private CaptureStoppedEventHandler captureStoppedEventHandler;

        private void Shutdown()
        {
            if (device != null)
            {
                device.StopCapture();
                device.Close();
                device.OnPacketArrival -= arrivalEventHandler;
                device.OnCaptureStopped -= captureStoppedEventHandler;
                device = null;

                // ask the background thread to shut down
                BackgroundThreadStop = true;

                // wait for the background thread to terminate
                backgroundThread.Join();

                // switch the icon back to the play icon
                //startStopToolStripButton.Image = global::WinformsExample.Properties.Resources.play_icon_enabled;
                //startStopToolStripButton.ToolTipText = "Select device to capture from";
            }
        }

        private void StartCapture(int deviceIndex)
        {
            packetCount = 0;
            device = CaptureDeviceList.Instance[deviceIndex];
            TinyPacketQueue = new Queue<TinyPacket>();
            LastStatisticsOutput = DateTime.Now;

            // start the background thread
            BackgroundThreadStop = false;
            backgroundThread = new System.Threading.Thread(BackgroundThread);
            backgroundThread.Start();

            // setup background capture
            arrivalEventHandler = new PacketArrivalEventHandler(device_OnPacketArrival);
            device.OnPacketArrival += arrivalEventHandler;
            captureStoppedEventHandler = new CaptureStoppedEventHandler(device_OnCaptureStopped);
            device.OnCaptureStopped += captureStoppedEventHandler;
            device.Open();
            device.Filter = "udp";

            // start the background capture
            device.StartCapture();

            // disable the stop icon since the capture has stopped
            //startStopToolStripButton.Image = global::WinformsExample.Properties.Resources.stop_icon_enabled;
            //startStopToolStripButton.ToolTipText = "Stop capture";
        }

        void device_OnCaptureStopped(object sender, CaptureStoppedEventStatus status)
        {
            if (status != CaptureStoppedEventStatus.CompletedWithoutError)
            {
                MessageBox.Show("Error stopping capture", "Error", MessageBoxButtons.OK, MessageBoxIcon.Error);
            }
        }


        void device_OnPacketArrival(object sender, CaptureEventArgs e)
        {
            // print out periodic statistics about this device
            var Now = DateTime.Now; // cache 'DateTime.Now' for minor reduction in cpu overhead
            var interval = Now - LastStatisticsOutput;
            if (interval > LastStatisticsInterval)
            {
                this.BeginInvoke(new MethodInvoker(delegate
                    {
                        uiPacketArrival.Text = "-ReceivedPackets: " + e.Device.Statistics.ReceivedPackets.ToString();
                    }
                    ));
                captureStatistics = e.Device.Statistics;
                statisticsUiNeedsUpdate = true;
                LastStatisticsOutput = Now;
            }

            // lock QueueLock to prevent multiple threads accessing PacketQueue at
            // the same time
            lock (QueueLock)
            {
                PacketQueue.Add(e.Packet);
            }
        }

        private void CaptureForm_Shown(object sender, EventArgs e)
        {
            deviceListForm.Show();
        }

        /// <summary>
        /// 
        /// Checks for queued packets. If any exist it locks the QueueLock, saves a
        /// reference of the current queue for itself, puts a new queue back into
        /// place into PacketQueue and unlocks QueueLock. This is a minimal amount of
        /// work done while the queue is locked.
        ///
        /// The background thread can then process queue that it saved without holding
        /// the queue lock.
        /// </summary>
        private void BackgroundThread()
        {
            while (!BackgroundThreadStop)
            {
                bool shouldSleep = true;

                lock (QueueLock)
                {
                    if (PacketQueue.Count != 0)
                    {
                        shouldSleep = false;
                    }
                }

                if (shouldSleep)
                {
                    System.Threading.Thread.Sleep(250);
                }
                else // should process the queue
                {
                    List<RawCapture> ourQueue;
                    lock (QueueLock)
                    {
                        // swap queues, giving the capture callback a new one
                        ourQueue = PacketQueue;
                        PacketQueue = new List<RawCapture>();
                    }
                    this.BeginInvoke(new MethodInvoker(delegate
                    {
                        uiQueueCount.Text = string.Format("BackgroundThread: ourQueue.Count is {0}", ourQueue.Count);
                    }
                    ));
                    foreach (var packet in ourQueue)
                    {
                        // Here is where we can process our packets freely without
                        // holding off packet capture.
                        //
                        // NOTE: If the incoming packet rate is greater than
                        //       the packet processing rate these queues will grow
                        //       to enormous sizes. Packets should be dropped in these
                        //       cases
                        TinyPacket tinyPacket = TinyPacket.TryParse(packet);
                        var time = packet.Timeval.Date;
                        var len = packet.Data.Length;
                        //处理IM数据包
                        if (tinyPacket.IsTinyIMPacket)
                        {
                            this.BeginInvoke(new MethodInvoker(delegate
                            {
                                TinyPacketQueue.Enqueue(tinyPacket);
                                tinyPacket.UpdateUIWithCapture(uiListViewPacket, tinyPacket);
                                string packetInfo = string.Format("BackgroundThread: {0}:{1}:{2},{3} Len={4}",
                             time.Hour, time.Minute, time.Second, time.Millisecond, len);
                                NetworkLog.Debug(packetInfo);
                                uiStatistics.Text = packetInfo;
                            }));
                        }
                    }
                }
            }
        }

        private void CaptureForm_FormClosing(object sender, FormClosingEventArgs e)
        {
            Shutdown();
           
        }
        private void dataGridView_SelectionChanged(object sender, EventArgs e)
        {
            //if (dataGridView.SelectedCells.Count == 0)
            //    return;

            //var packetWrapper = (PacketWrapper)dataGridView.Rows[dataGridView.SelectedCells[0].RowIndex].DataBoundItem;
            //var packet = Packet.ParsePacket(packetWrapper.p.LinkLayerType, packetWrapper.p.Data);
            //packetInfoTextbox.Text = packet.ToString(StringOutputType.VerboseColored);
        }

        private void CaptureForm_Load(object sender, EventArgs e)
        {
            uiListViewPacket.SelectedIndexChanged += new EventHandler(uiListViewPacket_SelectedIndexChanged);
            deviceListForm = new DeviceListForm();
            deviceListForm.OnItemSelected += new DeviceListForm.OnItemSelectedDelegate(deviceListForm_OnItemSelected);
            deviceListForm.OnCancel += new DeviceListForm.OnCancelDelegate(deviceListForm_OnCancel);
            this.WindowState = FormWindowState.Minimized;
        }
    }

}
